﻿using System;
using System.IO;
using System.Threading;
using System.Xml;
using Sasa.QualityTools.CodeAnalysis.Common;

namespace Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.Models
{
    public class CodeAnalysisConfiguration
    {
        private string configurationPath;

        private CodeAnalysisConfiguration parent;

        private bool enableSearchGac;

        private bool ignoreGeneratedCode;

        private double timeout;

        private string ruleSet;

        private bool enableCodeAnalysisOnBuild;


        public string ConfigurationPath { get { return configurationPath; } }

        public bool EnableCodeAnalysis { get; set; }

        public bool OverrideEnableSearchGac { get; set; }

        public bool OverrideIgnoreGeneratedCode { get; set; }

        public bool OverrideTimeout { get; set; }

        public bool OverrideRuleSet { get; set; }

        public bool OverrideEnableCodeAnalysisOnBuild { get; set; }

        public bool EnableSearchGac
        {
            get { return (parent != null && !OverrideEnableSearchGac) ? parent.EnableSearchGac : enableSearchGac; }
            set { enableSearchGac = value; }
        }

        public bool IgnoreGeneratedCode
        {
            get { return (parent != null && !OverrideIgnoreGeneratedCode) ? parent.IgnoreGeneratedCode : ignoreGeneratedCode; }
            set { ignoreGeneratedCode = value; }
        }

        public double Timeout
        {
            get { return (parent != null && !OverrideTimeout) ? parent.Timeout : timeout; }
            set { timeout = value; }
        }

        public string RuleSet
        {
            get { return (parent != null && !OverrideRuleSet) ? parent.RuleSet : ruleSet; }
            set { ruleSet = value; }
        }

        public bool EnableCodeAnalysisOnBuild
        {
            get { return (parent != null && !OverrideEnableCodeAnalysisOnBuild) ? parent.EnableCodeAnalysisOnBuild : enableCodeAnalysisOnBuild; }
            set { enableCodeAnalysisOnBuild = value; }
        }


        public CodeAnalysisConfiguration(string configurationPath)
        {
            this.configurationPath = Path.GetFullPath(configurationPath);
            EnableCodeAnalysis = true;
            enableSearchGac = true;
            ignoreGeneratedCode = true;
            timeout = 60;
        }


        public static CodeAnalysisConfiguration Load(string configurationPath)
        {
            Logger.Info("Loading code analysis configuration from '{0}'.", configurationPath);

            var config = new CodeAnalysisConfiguration(configurationPath);
            if (!File.Exists(configurationPath))
            {
                return config;
            }

            using (var stream = new FileStream(configurationPath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                Load(stream, config);
            }
            return config;
        }

        internal static void Load(Stream stream, CodeAnalysisConfiguration configuration)
        {
            var xml = new XmlDocument();
            xml.Load(stream);

            configuration.EnableCodeAnalysis
                = Fetch<bool>(xml, "/CodeAnalysisConfiguration/@Enable");

            configuration.OverrideEnableSearchGac
                = Fetch<bool>(xml, "/CodeAnalysisConfiguration/EnableSearchGAC/@Override");
            configuration.EnableSearchGac
                = Fetch<bool>(xml,"/CodeAnalysisConfiguration/EnableSearchGAC/@Value");

            configuration.OverrideIgnoreGeneratedCode
                = Fetch<bool>(xml,"/CodeAnalysisConfiguration/IgnoreGeneratedCode/@Override");
            configuration.IgnoreGeneratedCode
                =Fetch<bool>(xml,"/CodeAnalysisConfiguration/IgnoreGeneratedCode/@Value");

            configuration.OverrideTimeout
                = Fetch<bool>(xml,"/CodeAnalysisConfiguration/Timeout/@Override");
            configuration.Timeout
                = Fetch<double>(xml,"/CodeAnalysisConfiguration/Timeout/@Value");

            var converter = new RuleSetConverter(Path.GetDirectoryName(configuration.ConfigurationPath));
            configuration.OverrideRuleSet
                = Fetch<bool>(xml,"/CodeAnalysisConfiguration/RuleSet/@Override");
            configuration.RuleSet
                = converter.Reverse(Fetch<string>(xml, "/CodeAnalysisConfiguration/RuleSet/@Value"));

            configuration.OverrideEnableCodeAnalysisOnBuild
                = Fetch<bool>(xml, "/CodeAnalysisConfiguration/EnableCodeAnalysisOnBuild/@Override");
            configuration.EnableCodeAnalysisOnBuild
                = Fetch<bool>(xml, "/CodeAnalysisConfiguration/EnableCodeAnalysisOnBuild/@Value");
        }

        private static T Fetch<T>(XmlDocument xml, string xpath)
        {
            var culture = Thread.CurrentThread.CurrentUICulture;
            var node = xml.SelectSingleNode(xpath);
            return (node != null) ? (T)Convert.ChangeType(node.Value, typeof(T), culture) : default(T);
        }

        public void Override(CodeAnalysisConfiguration parent)
        {
            this.parent = parent;
        }

        public void Save()
        {
            Logger.Info("Saving code analysis configuration to '{0}'.", configurationPath);

            Directory.CreateDirectory(Path.GetDirectoryName(configurationPath));
            using (var stream = new FileStream(configurationPath, FileMode.Create, FileAccess.Write, FileShare.Write))
            {
                Save(stream);
            }
        }

        internal void Save(Stream stream)
        {
            var settings = new XmlWriterSettings();
            settings.Indent = true;
            using (var writer = XmlWriter.Create(stream, settings))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement("CodeAnalysisConfiguration");
                WriteRoot(writer);
                WriteValue(writer, "EnableSearchGAC", OverrideEnableSearchGac, enableSearchGac);
                WriteValue(writer, "IgnoreGeneratedCode", OverrideIgnoreGeneratedCode, ignoreGeneratedCode);
                WriteValue(writer, "Timeout", OverrideTimeout, timeout);
                WriteRuleSetValue(writer, OverrideRuleSet, ruleSet);
                WriteValue(writer, "EnableCodeAnalysisOnBuild", OverrideEnableCodeAnalysisOnBuild, enableCodeAnalysisOnBuild);
                writer.WriteEndElement();
                writer.WriteEndDocument();
            }
        }

        private void WriteRoot(XmlWriter writer)
        {
            writer.WriteStartAttribute("Version");
            writer.WriteValue("1.3.0");
            writer.WriteEndAttribute();

            writer.WriteStartAttribute("Enable");
            writer.WriteValue(EnableCodeAnalysis);
            writer.WriteEndAttribute();
        }

        private static void WriteValue(XmlWriter writer, string elementName, bool isOverride, object value)
        {
            writer.WriteStartElement(elementName);
            writer.WriteStartAttribute("Override");
            writer.WriteValue(isOverride);
            writer.WriteEndAttribute();
            writer.WriteStartAttribute("Value");
            writer.WriteValue(value ?? "");
            writer.WriteEndAttribute();
            writer.WriteEndElement();
        }

        private void WriteRuleSetValue(XmlWriter writer, bool isOverride, object value)
        {
            writer.WriteStartElement("RuleSet");
            writer.WriteStartAttribute("Override");
            writer.WriteValue(isOverride);
            writer.WriteEndAttribute();

            if (value != null)
            {
                var converter = new RuleSetConverter(Path.GetDirectoryName(configurationPath));
                writer.WriteStartAttribute("Value");
                writer.WriteValue(converter.Convert(value.ToString()) ?? "");
                writer.WriteEndAttribute();
            }

            writer.WriteEndElement();
        }
    }
}
